/**
 * class-schedule v1.0.1
 * Includes class-schedule.js
 * Copyright 2018-2117, class-schedule Foundation, Inc. and other contributors
 * 作者: 黄驰宇 84286167@qq.com
 *
 * @desc 基本功能
 * 初始化日历事件,可选 loading 遮罩
 * 点击年份,渲染日历 (可选,回调函数, 如果有回调:出现loading 遮罩)
 * 点击月份,渲染日历,(可选,回调函数)
 *
 * 年份,月份,只创建一次
 *
 * 样式放在外面处理,这样会更灵活
 * */
/**
 * @desc 插件架构
 * 1 本月第一天是星期几: 1 号放在哪个位置; 星期几 - 1 = 前面补位的天数
 * 2 上个月有多少天: 如果是一月份, 就要算去年的12月
 * 3 上个月最后N天是什么: 用于日历前面的补位
 * 4 本月最后一天是星期几: 用于补下个月的天数
 * */
;(function (root, document, jQuery, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        // console.log("amd");
        define(['jquery'], factory(root, document, jQuery)); // requireJs在当前目录引入jquery
    } else if (typeof exports === 'object') {
        // Node/CommonJS.
        // console.log("commonJs");
        module.exports = factory(root, document, jQuery);
    } else {
        // 浏览器(Browser globals)
        // console.log("browser");
        factory(root, document, jQuery);
    }
    // 匿名函数 (function 作为参数) , root 是根对象: JS 是window , nodeJS 是 global
})(typeof global !== "undefined" ? global : this.window || this.global, document, jQuery, function (root, document, $, undefined) {

    // 验证是否引用jquery
    if (!root.jQuery) {
        alert('请先引入jQuery,再使用class-schedule');
        return;
    }

    var doc = document, $root = $(root), pluginName = "classSchedule";


    // 默认参数
    /**
     * 1 分成3个容器展示: "年份切换","月份","日期"
     * */
    var DEFAULTS = {
        eleYear: "#eleYear",
        eleMonth: "#eleMonth",
        eleDay: "#eleDay",
        yearClickCallBack: false,  // 点击年的回调
        monthClickCallBack: false, // 点击月的回调
        dayClickCallBack: false,
    };


    var ClassSchedule = function (options) {
        var opt = $.extend({}, DEFAULTS, options); // 合并后的参数
        var obj = {
            // loading: "", // 创建的jquery 对象
            nowYear: 2017, // 当前年份,动态修改
            nowMonth: 6, // 当前月份,点击事件动态修改

            // 1 ========= 基本元素生成 "年,月,日 =========="

            // 1.1 年份生成
            createYear: function (nowYear) {
                var _this = this;
                var year = $("<div class='year-setting'>" +
                    "<span class='minus glyphicon glyphicon-chevron-left'></span>" +
                    "<span class='year-num'>" + (nowYear || _this.nowYear) + "</span>" +
                    "<span class='plus glyphicon glyphicon-chevron-right'></span>" +
                    "</div>");
                $(opt.eleYear).html(year);
            },
            // 1.2 月份生成
            createMonth: function (nowMonth) {
                var month = $("<div class='month-setting'>" +
                    "<div data-month='1' class='month-item month-1'>一月</div>" +
                    "<div data-month='2' class='month-item month-2'>二月</div>" +
                    "<div data-month='3' class='month-item month-3'>三月</div>" +
                    "<div data-month='4' class='month-item month-4'>四月</div>" +
                    "<div data-month='5' class='month-item month-5'>五月</div>" +
                    "<div data-month='6' class='month-item month-6'>六月</div>" +
                    "<div data-month='7' class='month-item month-7'>七月</div>" +
                    "<div data-month='8' class='month-item month-8'>八月</div>" +
                    "<div data-month='9' class='month-item month-9'>九月</div>" +
                    "<div data-month='10' class='month-item month-10'>十月</div>" +
                    "<div data-month='11' class='month-item month-11'>十一月</div>" +
                    "<div data-month='12' class='month-item month-12'>十二月</div>" +
                    "</div>");

                // 初始化激活按钮
                month.find(".month-" + (nowMonth || this.nowMonth)).addClass("month-active").siblings().removeClass("month-active");
                $(opt.eleMonth).html(month);
            },
            // 1.3 日期生成
            createDay: function (year, month, firstDay) {
                var _this = this;
                var right = $('<div class="right-table"></div>');
                var table = $('<table class="table"><thead><th>一</th> <th>二</th> <th>三</th> <th>四</th><th>五</th> <th>六</th> <th>日</th> </thead> </table>');
                var tbody = $("<tbody></tbody>");

                // 日历前面补位 (需补充: 考虑关于上个月, 最后几天是星期天的问题)
                var trFirst = $("<tr></tr>"); // 第一排
                var supplement = firstDay - 1; // 第一排补充的条数

                // 处理第一排的数据
                if (supplement) {
                    // 01 上个月的天数
                    var days = 0;
                    if (month - 1 == 0) {
                        days = _this.prevMonthDay(year - 1, 12);
                    } else {
                        days = _this.prevMonthDay(year, month - 1);
                    }

                    // 02 补充上个月的天数
                    for (var i = 0; i < supplement; i++) {
                        var tdFirst = $('<td><span class="dayNum  prev-month">' + (days - i) + '</span></td>');
                        trFirst.prepend(tdFirst);
                    }

                    // 03 填充第一排的本月天数
                    var currentMonthDay = 7 - supplement;
                    for (var j = 0; j < currentMonthDay; j++) {
                        var tdCurrentDay = $('<td><span class="dayNum  current-month">' + (j + 1) + '</span></td>');
                        trFirst.append(tdCurrentDay);
                    }
                    // console.log("已补位:", supplement, " 条");
                }
                else {
                    // 1号正好是星期一, 不用补位
                    trFirst = $(
                        '<tr>' +
                        '<td><span class="dayNum  current-month">1</span></td>' +
                        '<td><span class="dayNum  current-month">2</span></td>' +
                        '<td><span class="dayNum  current-month">3</span></td>' +
                        '<td><span class="dayNum  current-month">4</span></td>' +
                        '<td><span class="dayNum  current-month">5</span></td>' +
                        '<td><span class="dayNum  current-month">6</span></td>' +
                        '<td><span class="dayNum  current-month">7</span></td>' +
                        '</tr>');
                }

                tbody.append(trFirst);

                // ======== 处理中间部分的数据 ================
                var currentMonth = _this.prevMonthDay(year, month); // 本月天数

                /** 第一排补位天数: firstDay-1
                 *  算出第一排本月渲染了多少条:  7 - (firstDay-1 ) = 第一排本月已渲染天数
                 *  中间部分渲染起始点: 第一排本月已渲染天数 + 1
                 */

                var startDay = 7 - (firstDay - 1) + 1; // 当前渲染的起始点
                var tr = $("<tr></tr>");
                var num = 1; // 对应排的个数
                for (var k = 1; k <= currentMonth; k++) {
                    // k 必须从1开始, 否则走不到30号.
                    if (k >= startDay) {
                        tr.append("<td><span class='dayNum current-month'>" + k + "</span></td>");
                        // 不能用k 取余(k=7的时候,这一排可能只放了5个), 应该用每一排的条数取余
                        if ((num % 7) == 0) {
                            tbody.append(tr);
                            tr = $("<tr></tr>");
                        }
                        // 最后一排,最后几个无法对7取余
                        else if (k == currentMonth) {
                            // ======= 最后一排补位 ==========
                            var lastDayWeek = _this.monthLastDayWeek(currentMonth, firstDay); // 补位条数
                            for (var n = 1; n <= lastDayWeek; n++) {
                                tr.append("<td><span class='dayNum next-month'>" + n + "</span></td>");
                            }
                            tbody.append(tr);
                        }
                        num++;
                    }
                }

                table.append(tbody);
                right.append(table);
                $(opt.eleDay).html("").append(right);
            },

            // 2 ========= 判断闰年 (leapYear) ==============
            leapYear: function (_year) {
                // 闰年: 2月份为29天 , 正常28天
                // 计算: 能被400 整除, 或者 能被4整除却不能被100整除
                if (_year % 400 === 0 || (_year % 4 === 0 && _year % 100 !== 0)) {
                    return true;
                }
                return false;
            },

            // 3 ========== 判断某年某月有几天 ==============
            prevMonthDay: function (year, month) {
                if (month == 2) {
                    // 如果是2月, 判断是否闰年
                    if (this.leapYear(year)) {
                        return 29;
                    } else {
                        return 28;
                    }
                } else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
                    return 31;
                } else {
                    return 30;
                }
            },

            // 4 ========== 判断某年某月的1号是星期几 ========
            getFirstDay: function (_year, _month) {
                var _this = this;
                var allDay = 0, y = _year - 1, i = 1;

                // 得出一年有多少天
                allDay = y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) + 1;

                for (; i < _month; i++) {
                    switch (i) {
                        case 1:
                            allDay += 31;
                            break;
                        case 2:
                            if (_this.leapYear(_year)) allDay += 29; else allDay += 28;
                            break;
                        case 3:
                            allDay += 31;
                            break;
                        case 4:
                            allDay += 30;
                            break;
                        case 5:
                            allDay += 31;
                            break;
                        case 6:
                            allDay += 30;
                            break;
                        case 7:
                            allDay += 31;
                            break;
                        case 8:
                            allDay += 31;
                            break;
                        case 9:
                            allDay += 30;
                            break;
                        case 10:
                            allDay += 31;
                            break;
                        case 11:
                            allDay += 30;
                            break;
                        case 12:
                            allDay += 31;
                            break;
                    }
                }
                return allDay % 7 || 7;
            },

            // 5 ========== 本月最后一天是星期几 ============
            monthLastDayWeek: function (days, firstDay) {
                // @days: 该月总天数,  @firstDay: 该月第一天是星期几
                // 需要测试:  rowFirst == 0 的情况 (未测试)

                var rowFirst = firstDay - 1; // 第一排补位条数
                var total = rowFirst + days;
                var rowNum = total % 7; // 最后一排条数
                // console.log("rowNum: ",rowNum );
                return 7 - rowNum; // 需要补位条数
            },

            // 6 =========== 点击事件动态渲染日历 ==========
            clickEvent: function () {
                var _this = this;
                // 年份的点击
                $(opt.eleYear).on("click", ".minus, .plus", function () {
                    var num = $(this).siblings(".year-num");
                    var text = Number(num.text());

                    if ($(this).hasClass("minus")) {
                        text = --text;
                    } else {
                        text = ++text;
                    }
                    num.text(text);
                    _this.nowYear = text;

                    _this.createDay(text, _this.nowMonth, _this.getFirstDay(text, _this.nowMonth));
                    opt.yearClickCallBack && opt.yearClickCallBack(text);
                });

                // 月份的点击
                $(opt.eleMonth).on("click", ".month-item", function () {
                    $(this).addClass("month-active").siblings().removeClass("month-active");
                    var month = $(this).data("month");
                    _this.nowMonth = month;

                    _this.createDay(_this.nowYear, _this.nowMonth, _this.getFirstDay(_this.nowYear, _this.nowMonth));
                    opt.monthClickCallBack && opt.monthClickCallBack(month);
                });

                // 日的点击事件
                $(opt.eleDay).on("click", ".working, .day-remove", function () {
                    $(this).toggleClass("working day-remove flag");
                    opt.dayClickCallBack && opt.dayClickCallBack(this);
                });
            },

            // 7 =========== 初始化日历 ===================
            initDate: function () {
                var _this = this;
                // 得到当前 年、月、日
                var today = new Date();
                var _year = today.getFullYear(),
                    _month = today.getMonth() + 1,
                    _day = today.getDate();

                this.nowYear = _year;
                this.nowMonth = _month;

                // 得到第一天是星期几
                var firstDay = this.getFirstDay(_year, _month);

                //创建日历
                this.createYear(_year);
                this.createMonth(_month);
                this.createDay(_year, _month, firstDay);
            }
        };

        obj.initDate();
        obj.clickEvent();
    };

    // 给jQuery 扩展方法
    $.fn[pluginName] = ClassSchedule;
});
